package rs.etf.pp1.symboltable.concepts;

import java.util.Collection;

import rs.etf.pp1.symboltable.Tab;
import rs.etf.pp1.symboltable.structure.HashTableDataStructure;
import rs.etf.pp1.symboltable.structure.SymbolDataStructure;
import rs.etf.pp1.symboltable.visitors.SymbolTableVisitor;

/**
 * Struktura tipa u MikroJavi.
 * 
 * @author ETF
 * 
 */
public class Struct {

	// kodiranje tipova
	public static final int None = 0;
	public static final int Int = 1;
	public static final int Char = 2;
	public static final int Array = 3;
	public static final int Class = 4;

	private int kind; // None, Int, Char, Array, Class

	private Struct elemType; // niz: tip elementa niza

	// klasa: broj bolja klase
	private int numOfFields;

	// klasa: referenca na hes tabelu u kojoj se nalaze polja klase
	private SymbolDataStructure members;

	public void setMembers(SymbolDataStructure symbols) {
		members = symbols;
		numOfFields = 0;
		if (symbols != null) {
			for (Obj s : symbols.symbols()) {
				if (s.getKind() == Obj.Fld)
					numOfFields++;
			}
		}
	}
	
	public Struct(int kind) {
		this.kind = kind;
	}

	public Struct(int kind, Struct elemType) {
		this.kind = kind;
		if (kind == Array) this.elemType = elemType;
	}

	public Struct(int kind, SymbolDataStructure members) {
		this.kind = kind;
		setMembers(members);
	}

	public int getKind() {
		return kind;
	}

	public Struct getElemType() {
		return elemType;
	}

	public int getNumberOfFields() {
		return numOfFields;
	}

	public SymbolDataStructure getMembers() {
		if (members == null)
			members = new HashTableDataStructure();
		return members;
	}

	public boolean equals(Object o) {
		// najpre provera da li su reference jednake
		if (super.equals(o)) return true;

		if (!(o instanceof Struct)) return false;

		return equals((Struct) o);
	}

	public boolean isRefType() {
		return kind == Class || kind == Array;
	}

	public boolean equals(Struct other) {
		if (kind == Array) return other.kind == Array
				&& elemType.equals(other.elemType);

		if (kind == Class) return other.kind == Class && numOfFields == other.numOfFields
				&& Obj.equalsCompleteHash(members, other.members);

		// mora biti isti Struct cvor
		return this == other;
	}

	public boolean compatibleWith(Struct other) {
		return this.equals(other) || this == Tab.nullType && other.isRefType()
				|| other == Tab.nullType && this.isRefType();
	}

	public boolean assignableTo(Struct dest) {
		return 	this.equals(dest) 
				|| 
				(this == Tab.nullType && dest.isRefType())
				|| 
				(this.kind == Array && dest.kind == Array && dest.elemType == Tab.noType);
	}
	
	public void accept(SymbolTableVisitor stv) {
		stv.visitStructNode(this);
	}
	
}
